package com.daffodil.flowable.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringTokenizer;

import javax.annotation.PostConstruct;

import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.ExclusiveGateway;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.SequenceFlow;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.impl.de.odysseus.el.ExpressionFactoryImpl;
import org.flowable.common.engine.impl.de.odysseus.el.util.SimpleContext;
import org.flowable.common.engine.impl.javax.el.ExpressionFactory;
import org.flowable.common.engine.impl.javax.el.ValueExpression;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceBuilder;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.daffodil.core.entity.Query;
import com.daffodil.core.exception.BaseException;
import com.daffodil.flowable.service.IFlowableAccountService;
import com.daffodil.framework.shiro.util.ShiroUtils;
import com.daffodil.system.entity.SysUser;
import com.daffodil.util.StringUtils;

/**
 * 流程辅助工具集合
 * 
 * @author yweijian
 * @date 2020年1月14日
 * @version 1.0
 */
@Component
public class FlowableUtils {

	private static String[] chart = { " ", "$", "{", "}", "(", ")", "'", "\"", "||", "&&", ">", "<", "==", ">=", "<=",
			"!=" };

	private static ProcessEngine processEngine;

	private static IFlowableAccountService accountService;

	@Autowired
	private ProcessEngine processEngineService;

	@Autowired
	private IFlowableAccountService flowableAccountService;

	@PostConstruct
	public void init() {
		processEngine = this.processEngineService;
		accountService = this.flowableAccountService;
	}

	/**
	 * 根据业务ID获取当前任务
	 * 
	 * @param businessKey
	 * @return Task
	 */
	public static Task getCurrentTask(String businessKey) {
		try{
			Task task = processEngine.getTaskService().createTaskQuery().processInstanceBusinessKey(businessKey)
					.includeProcessVariables()
					.active()
					.singleResult();
			return task;
		}catch (Exception e) {
			throw new BaseException("流程任务获取失败，请联系管理员");
		}
	}

	/**
	 * 获取流程的顺序节点
	 * 
	 * @param businessKey
	 * @return List [ SequenceFlow ]
	 */
	public static List<SequenceFlow> getSequenceFlowNode(String businessKey) {
		Task task = getCurrentTask(businessKey);
		if (null == task) {
			return null;
		}
		ExecutionEntity execution = (ExecutionEntity) processEngine.getRuntimeService().createExecutionQuery()
				.executionId(task.getExecutionId()).singleResult();
		BpmnModel bpmnModel = processEngine.getRepositoryService().getBpmnModel(task.getProcessDefinitionId());
		FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(execution.getActivityId());
		return flowNode.getOutgoingFlows();
	}

	/**
	 * 获取当前流程节点
	 * 
	 * @param businessKey
	 * @return FlowElement
	 */
	public static FlowElement getCurrentFlowNode(String businessKey) {
		List<SequenceFlow> outFlows = getSequenceFlowNode(businessKey);
		if (StringUtils.isEmpty(outFlows)) {
			return null;
		}
		return outFlows.get(0).getSourceFlowElement();
	}

	/**
	 * 根据流转操作ID获取下一流程节点
	 * 
	 * @param businessKey
	 *            业务ID
	 * @param handleId
	 *            操作ID
	 * @return
	 */
	public static FlowElement getNextFlowNode(String businessKey, String handleId) {
		List<SequenceFlow> outFlows = getSequenceFlowNode(businessKey);
		return getNextFlowNode(outFlows, businessKey, handleId);
	}

	/**
	 * 获取下一流程节点
	 * 
	 * @param outFlows
	 *            输出流集合
	 * @param handleId
	 *            操作ID
	 * @return
	 */
	public static FlowElement getNextFlowNode(List<SequenceFlow> outFlows, String businessKey, String handleId) {
		if (StringUtils.isEmpty(outFlows) || StringUtils.isEmpty(handleId)) {
			return null;
		}
		for (SequenceFlow sequenceFlow : outFlows) {
			if (handleId.equals(sequenceFlow.getId())) {
				if (isExclusiveGatewayNode(sequenceFlow.getTargetFlowElement())) {
					ExclusiveGateway exclusiveGateway = (ExclusiveGateway) sequenceFlow.getTargetFlowElement();
					List<SequenceFlow> gatewayOutFlows = exclusiveGateway.getOutgoingFlows();
					Map<String, Object> processVariables = getCurrentTask(businessKey).getProcessVariables();
					if(StringUtils.isEmpty(processVariables)){//没有变量附加值,获取默认第一条
						return getNextFlowNode(gatewayOutFlows, businessKey, gatewayOutFlows.get(0).getId());
					}else{
						for(SequenceFlow flow : gatewayOutFlows){
							String conditionExpression = flow.getConditionExpression();
							if(checkConditionExpression(conditionExpression, processVariables)){//匹配到符合条件的输出流方向
								return getNextFlowNode(gatewayOutFlows, businessKey, flow.getId());
							}
						}
					}
				} else {
					return sequenceFlow.getTargetFlowElement();
				}
			}
		}
		return null;
	}
	
	/** 
     * 根据key和value判断el表达式是否通过信息  
     * @param String key    el表达式key信息  
     * @param String el     el表达式信息  
     * @param String value  el表达式传入值信息  
     * @return 
     */ 
	public static boolean checkConditionExpression(String conditionExpression, Map<String, Object> processVariables) {  
        ExpressionFactory factory = new ExpressionFactoryImpl();    
        SimpleContext context = new SimpleContext();
        Iterator<Entry<String, Object>> it = processVariables.entrySet().iterator();
        while(it.hasNext()){
        	Map.Entry<String, Object> entry = it.next();
        	context.setVariable(entry.getKey(), factory.createValueExpression(entry.getValue(), Object.class)); 
        }
        ValueExpression expression = factory.createValueExpression(context, conditionExpression, boolean.class);    
        return (Boolean) expression.getValue(context);  
    }  

	/**
	 * 获取下一步的操作动作集合
	 * 
	 * @param businessKey
	 * @return List [ Map [ String, name ] ]
	 */
	public static List<Map<String, String>> getNextHandle(String businessKey) {
		List<SequenceFlow> outFlows = getSequenceFlowNode(businessKey);
		if (StringUtils.isEmpty(outFlows)) {
			return null;
		}
		List<Map<String, String>> handles = new ArrayList<Map<String, String>>();
		for (SequenceFlow sequenceFlow : outFlows) {
			Map<String, String> map = new HashMap<String, String>();
			map.put("id", sequenceFlow.getId());
			map.put("name", sequenceFlow.getName());
			handles.add(map);
		}
		return handles;
	}

	/**
	 * 根据业务ID和操作名称获取流转操作条件
	 * 
	 * @param businessKey
	 * @param handleId
	 * @return Map [ String, Object ]
	 */
	public static Map<String, Object> getHandleVariables(String businessKey, String handleId) {
		List<SequenceFlow> outFlows = getSequenceFlowNode(businessKey);
		if (StringUtils.isEmpty(outFlows)) {
			return null;
		}
		for (SequenceFlow sequenceFlow : outFlows) {
			if (handleId.contentEquals(sequenceFlow.getId())) {
				return formatExpression(sequenceFlow.getConditionExpression());
			}
		}
		return null;
	}

	/**
	 * 条件表达式转换
	 * 
	 * @param expression
	 *            ${action == '通过' || bction > 0 && cction < 10 && dction !=
	 *            '员工'}
	 * @return Map {action : '通过', bction : 0, cction : 10, dction : '员工'}
	 */
	public static Map<String, Object> formatExpression(String expression) {
		if (StringUtils.isEmpty(expression)) {
			return null;
		}
		List<String> charts = new ArrayList<String>(chart.length);
		Collections.addAll(charts, chart);

		Map<String, Object> map = new HashMap<String, Object>();
		StringTokenizer tokens = new StringTokenizer(expression, "${}()'\", \n\r\f\t", true);
		String key = "", value = "";
		while (tokens.hasMoreTokens()) {
			String token = tokens.nextToken();
			if (!charts.contains(token) && "".equals(key)) {
				key = token;
			} else if (!charts.contains(token) && "".equals(value)) {
				value = token;
			}
			if (!"".equals(key) && !"".equals(value)) {
				map.put(key, value);
				key = "";
				value = "";
			}
		}
		return map;
	}

	/**
	 * 是否是开始节点
	 * 
	 * @param flowElement
	 * @return boolean
	 */
	public static boolean isStartEventNode(FlowElement flowElement) {
		return flowElement instanceof StartEvent;
	}

	/**
	 * 是否是用户任务节点
	 * 
	 * @param flowElement
	 * @return boolean
	 */
	public static boolean isUserTaskNode(FlowElement flowElement) {
		return flowElement instanceof UserTask;
	}

	/**
	 * 是否是网关节点
	 * 
	 * @param flowElement
	 * @return boolean
	 */
	public static boolean isExclusiveGatewayNode(FlowElement flowElement) {
		return flowElement instanceof ExclusiveGateway;
	}

	/**
	 * 是否是结束节点
	 * 
	 * @param flowElement
	 * @return boolean
	 */
	public static boolean isEndEventNode(FlowElement flowElement) {
		return flowElement instanceof EndEvent;
	}

	/**
	 * 获取下一节点的办理人员 从任务代理人、候选人、候选组选取下一步的办理人员
	 * 
	 * @param businessKey
	 *            业务ID
	 * @param handleId
	 *            任务操作ID
	 * @return List [ SysUser ]
	 */
	public static List<SysUser> getNextNodeHandleUser(String businessKey, String handleId) {
		FlowElement flowElement = getNextFlowNode(businessKey, handleId);
		List<SysUser> users = null;
		if (isUserTaskNode(flowElement)) {
			UserTask userTask = (UserTask) flowElement;
			String assignee = userTask.getAssignee();
			// 单用户
			if (StringUtils.isNotEmpty(assignee)) {
				if ("$INITIATOR".equals(assignee)) {// 流程的发起者
					ProcessInstance processInstance = processEngine.getRuntimeService().createProcessInstanceQuery()
							.processInstanceBusinessKey(businessKey).active().singleResult();
					assignee = processInstance.getStartUserId();
				}
				SysUser sysUser = accountService.selectUserByLoginName(assignee);
				if (null != sysUser) {
					users = new ArrayList<SysUser>();
					users.add(sysUser);
					return users;
				}
			}
			// 多用户
			List<String> candidateUsers = userTask.getCandidateUsers();
			if (StringUtils.isNotEmpty(candidateUsers)) {
				Query<SysUser> query = new Query<SysUser>();
				query.setEntity(new SysUser());
				Map<String, Object> params = new HashMap<String, Object>();
				params.put("loginNames", candidateUsers);
				query.setParams(params);
				query.setOrderBy("createTime desc ");
				return accountService.selectUserListByLoginName(query);
			}
			// 多用户角色（组）
			List<String> candidateGroups = userTask.getCandidateGroups();
			if (StringUtils.isNotEmpty(candidateGroups)) {
				Query<SysUser> query = new Query<SysUser>();
				SysUser sysUser = new SysUser();
				sysUser.setRoleIds(candidateGroups.toArray(new String[candidateGroups.size()]));
				query.setEntity(sysUser);
				query.setOrderBy("createTime desc ");
				return accountService.selectUserListByRoleIds(query);
			}
		}
		return users;
	}

	/**
	 * 初始化流程任务
	 * 
	 * @param name
	 *            任务名称（一般为业务名称）
	 * @param name
	 * 			  流程类型ID（租户ID）
	 * @param processDefinitionKey
	 *            流程标识
	 * @param businessKey
	 *            业务ID
	 * @param userId
	 *            操作人
	 * @param variables
	 *            附加变量值
	 */
	public static String startProcessInstanceByKey(String name, String tenantId, String processDefinitionKey, String businessKey,
			Map<String, Object> variables) {
		if (StringUtils.isEmpty(processDefinitionKey) || StringUtils.isEmpty(businessKey)) {
			throw new BaseException("流程初始化失败，流程标识或业务ID为空");
		}
		processEngine.getIdentityService().setAuthenticatedUserId(ShiroUtils.getLoginName());
		ProcessInstanceBuilder builder = processEngine.getRuntimeService().createProcessInstanceBuilder();
		builder.name(name);
		builder.tenantId(tenantId);
		builder.processDefinitionKey(processDefinitionKey);
		builder.businessKey(businessKey);
		builder.variables(variables);
		ProcessInstance instance = builder.start();
		Task task = processEngine.getTaskService().createTaskQuery().processInstanceId(instance.getId()).singleResult();
		if (null != task) {
			processEngine.getTaskService().setAssignee(task.getId(), ShiroUtils.getLoginName());
		}
		
		return instance.getId();
	}
	
	/**
	 * 初始化流程任务
	 * 
	 * @param name
	 *            任务名称（一般为业务名称）
	 * @param name
	 * 			  流程类型ID（租户ID）
	 * @param processDefinitionId
	 *            流程部署ID
	 * @param businessKey
	 *            业务ID
	 * @param userId
	 *            操作人
	 * @param variables
	 *            附加变量值
	 */
	public static String startProcessInstanceById(String name, String tenantId, String processDefinitionId, String businessKey,
			Map<String, Object> variables) {
		if (StringUtils.isEmpty(processDefinitionId) || StringUtils.isEmpty(businessKey)) {
			throw new BaseException("流程初始化失败，流程ID或业务ID为空");
		}
		processEngine.getIdentityService().setAuthenticatedUserId(ShiroUtils.getLoginName());
		ProcessInstanceBuilder builder = processEngine.getRuntimeService().createProcessInstanceBuilder();
		builder.name(name);
		builder.tenantId(tenantId);
		builder.processDefinitionId(processDefinitionId);
		builder.businessKey(businessKey);
		builder.variables(variables);
		ProcessInstance instance = builder.start();
		Task task = processEngine.getTaskService().createTaskQuery().processInstanceId(instance.getId()).singleResult();
		if (null != task) {
			processEngine.getTaskService().setAssignee(task.getId(), ShiroUtils.getLoginName());
		}
		
		return instance.getId();
	}
}
